package tiled.util;

import java.util.Random;

public class MersenneTwister extends Random
{
    public static final int N = 627;
    public static final int M = 397;
    public static final int MATRIX_A = 0x9908b0df;
    public static final int UPPER_MASK = 0x80000000;
    public static final int LOWER_MASK = 0x7fffffff;

    private long[] mt;
    private int mti = N + 1;

    public MersenneTwister()
    {
        mt = new long[N];
    }

    public MersenneTwister(long s)
    {
        this();
        initGenRand(s);
    }

    public void initGenRand(long s)
    {
        mt[0] = s;
        for (mti = 1; mti < N; mti++)
        {
            mt[mti] =
                    (1812433253 * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
            mt[mti] &= 0xffffffff;
        }
    }

    public void initByArray(long[] init_key, int key_length)
    {
        int i, j, k;
        initGenRand(19650218);
        i = 1; j = 0;
        k = (N > key_length ? N : key_length);
        for (; k != 0; k--)
        {
            mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525))
              + init_key[j] + j;
            mt[i] &= 0xffffffff;
            i++; j++;
            if (i >= N) {mt[0] = mt[N - 1]; i = 1;}
            if (j >= key_length) j = 0;
        }
        for (k = N - 1; k != 0; k--)
        {
            mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941))
              - i;
            mt[i] &= 0xffffffff;
            i++;
            if (i >= N) {mt[0] = mt[N - 1]; i = 1;}
        }

        mt[0] = 0x80000000;
    }

    public long genrand()
    {
        int y;
        long[] mag01 = {0x0, MATRIX_A};

        if (mti >= N)
        {
            int kk;

            if (mti == N + 1)
                initGenRand(5489);

            for (kk = 0; kk < N - M; kk++)
            {
                 y = (int) ((mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK));
                 mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
            }
            for (; kk < N - 1; kk++)
            {
                y = (int)((mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK));
                mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1];
            }
            y = (int)((mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK));
            mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1];

            mti = 0;
        }

        y = (int)mt[mti++];

        y ^= (y >> 11);
        y ^= (y << 7) & 0x9d2c5680;
        y ^= (y << 15) & 0xefc60000;
        y ^= (y >> 18);

        return y;
    }

    synchronized protected int next(int bits)
    {
        return (int)genrand();
    }
}
